# -*- coding: utf-8 –*-
import datetime
import os
import json

from mongoengine import StringField, IntField, EmailField, BooleanField, \
    DateTimeField, EmbeddedDocumentField, ReferenceField, Document, \
    EmbeddedDocument, DecimalField, DictField, ListField, ObjectIdField, signals
from flask.ext.login import current_app

from .product import Product, ProductSKU
from .base import BaseDocument

source_choices = (
    ('breadtrip', '面包旅行'),
    ('vacation', '面包自由行'),
    ('micromessenger', '微信'),
    ('weibo', '微博'),
    ('other', '其他渠道'),
)

SEX_CHOICES = (
    ('male', "男性"),
    ('female', "女性"),
    ('secret', "保密"),
)


# vacation_finance_voucher_dir = current_app.config[
#     'VACATION_FINANCE_VOUCHER_DIR']


class Payment(Document):
    """
    支付
    """
    # plan = ReferenceField(UserPlan) # 用户计划是一个抽象概念，跟Payment不相关
    plan_id = ObjectIdField(verbose_name=u'用户计划')  # 暂时保存数据，保证数据一致性
    price = DecimalField(verbose_name=u'价格', precision=2)
    pay_type = IntField(verbose_name=u'付款类型')  # 付款类型: 0 支付宝 1微信 2 U付
    name = StringField(verbose_name=u'名称')
    orders = StringField(verbose_name=u'关联订单ids')
    thirdparty_payment_id = StringField(verbose_name=u'交易流水号', max_length=64)
    status = IntField(verbose_name=u'支付状态')

    return_msg = StringField(verbose_name=u'交易返回信息')
    user = IntField(verbose_name=u'用户ID')
    device_id = StringField(max_length=128)
    is_used_sale = BooleanField(default=False)
    is_active = BooleanField(default=True)
    date_added = DateTimeField(
        default=datetime.datetime.utcnow)  # old: date_added
    pay_date = DateTimeField(verbose_name='付款日期')  # 将order的字段移到这里

    meta = {
        'indexes': [
            {
                'fields': ['$status'],
                'default_language': 'english',
                # 'weights': {'title': 10, 'content': 2}
            }
        ],
        'collection': 'vacation_payment'
    }


class OrderItem(Document):
    """
    # OrderProductType -> OrderItem
    # ProductType -> ProductSKU
    产品清单
    """
    # TODO 考虑押金订单的设计, 面包旅行APP 使用子订单设计，用于区分优惠券、积分、押金订单

    order = ReferenceField('Order')
    product = ReferenceField(Product)  # 一个主产品
    # type -> product_sku
    product_sku = ReferenceField(ProductSKU)  # 一个单品,且应该自带价格，概念可以参考百度百科
    count = IntField(verbose_name=u'购买数量')

    # time -> 已经废弃
    # 影响价格的因数
    price_factor = DictField(verbose_name=u'价格因数')

    # fee 应该存放在product_sku里，这里用于保存旧设计里的数据
    fee = DecimalField(verbose_name=u'产品价格', precision=2)

    # info --  1. 如果是保险产品，需要用户填写投保人信息、一个或多个被保人信息等；
    #          2. 这里info 应该存该单品的信息
    #          3. 迁移数据，保存原OrderProductType中的字段：name，name_en,description, fee, count, category, use_days
    info = DictField(verbose_name=u'信息')

    @property
    def get_price(self):
        price = self.product_sku.fee * self.count
        if self.use_days:
            price *= price

        return price

    meta = {
        'collection': 'vacation_orderitem'
    }


class OrderFinance(EmbeddedDocument):
    """
    订单中有关财务的信息
    """
    refund_account = StringField(verbose_name=u'退款账户')
    pay_date = DateTimeField(verbose_name=u'付款日期')  # 财务部付款时间
    to_finance_date = DateTimeField(verbose_name=u'提交财务日期')
    # TODO max_digits 替换成 mongoengine的字段
    finance_price = DecimalField(verbose_name=u'财务支付价格', precision=2)
    currency = StringField(max_length=3, default='CNY')
    image = StringField(verbose_name=u'财务给供应商的结款凭证')  # 图片的文件地址
    finance_pay = DateTimeField(verbose_name=u'财务付款日期')
    description = StringField(verbose_name=u'描述')


class Order(BaseDocument):
    """
    参考京东的订单信息，一些属于产品信息的，要从订单剥离
    """

    # plan = ReferenceField(UserPlan) # 用户区分直接下订单和通过计划下订单,目前通过PlanOrderMtu实现, 就数据保存在order.info
    plan_id = StringField()  # 用户区分直接下订单和通过计划下订单
    # supplier = models.ForeignKey(Supplier)  # 供应商应该和产品关联
    product = ReferenceField(Product)  # 旧设计是如此

    # TODO 用户ID，应该转成ObjectId
    user_id = IntField(verbose_name=u'用户ID')  # 调用北京用户系统的API获取信息
    status = IntField(verbose_name=u'订单状态', default=1)  # TODO 需要预订定义状态码

    # 联系人/购买人信息 -- 未登录状态下，要求用户填写
    first_name = StringField(verbose_name=u'护照英文名')  # old: first_name
    last_name = StringField(verbose_name=u'护照英文姓')  # old: last_name
    contact_name_en = StringField(verbose_name=u'英文全名')
    contact_name = StringField(verbose_name=u'联系人')
    contact_tel = StringField(verbose_name=u'联系电话')
    contact_email = EmailField(verbose_name=u'Email地址')
    contact_address = StringField(verbose_name=u'联系地址')
    sex = StringField(verbose_name=u'性别', choices=SEX_CHOICES)

    # 付款信息
    payment_method = StringField(verbose_name=u'付款方式')
    commodity_price = DecimalField(verbose_name=u'商品金额', precision=2)
    freight = DecimalField(verbose_name=u'运费金额', precision=2)
    real_freight = DecimalField(verbose_name=u'实际运费', precision=2)
    coupon = DecimalField(verbose_name=u'优惠券金额', precision=2)
    amount_discount = DecimalField(verbose_name=u'优惠总金额', precision=2)
    gift_card = DecimalField(verbose_name=u'礼品卡金额', precision=2)
    payable_amount = DecimalField(verbose_name=u'应支付金额', precision=2)
    balance = DecimalField(verbose_name=u'余额', precision=2)
    payment_time = DateTimeField(verbose_name=u'付款时间')
    payment_no = StringField(verbose_name=u'支付单号')
    payment_source = StringField(verbose_name=u'支付来源', choices=source_choices)
    price = DecimalField(verbose_name=u'金额', precision=2)

    # 收货人信息
    consignee = StringField(verbose_name=u'收货人')
    consignee_address = StringField(verbose_name=u'地址')
    consignee_mobile_no = StringField(verbose_name=u'手机号码')
    consignee_telephone = StringField(verbose_name=u'固定电话')
    consignee_mail_address = EmailField(verbose_name=u'电子邮件')

    # 支付及配送方式
    # 支付方式 - payment_method
    # 运费 - real_freight
    delivery_date = DateTimeField(verbose_name=u'送货日期')
    delivery_method = StringField(verbose_name=u'送货方式')
    freighter = StringField(verbose_name=u'承运人')  # 如 顺风快递
    express_no = StringField(verbose_name=u'运单号')  # old: express

    # 发票
    invoice_type = StringField(verbose_name=u'发票类型')  # 可选：电子发票
    invoice_title = StringField(verbose_name=u'发票抬头')  # 个人/公司单位名
    invoice_content = StringField(verbose_name=u'发票内容')  # 可选：明细

    # 财务
    finance = EmbeddedDocumentField(OrderFinance)

    # 商品清单
    # 酒店、wifi、保险， 供应商凭证等信息，可以动态添加到OrderItem的info字段
    items = ListField(ReferenceField(OrderItem))

    # 交易
    payment = ReferenceField(Payment)

    # 信息， 这里的info字段，用于保存原订单中需要用户或者供应商注意的信息
    info = DictField(verbose_name=u'信息',
                     help_text=u'保存购买该产品的用户填写的信息或者购买成功后与产品'
                               u'有关联的信息，比如保险单号')
    info_recognizee = DictField(verbose_name=u'保险信息')

    # 详情， 用于保存旧设计的外键信息(OrderDetail), 新设计应该保存在OrderItem.info，或者SKU中
    detail = DictField(verbose_name=u'详情')

    # 其他
    device_id = StringField()  # 设备唯一IP，为了处理用户未登录时提交的订单, 在用户登录时能关联到用户的订单里
    unique_id = StringField(max_length=40, help_text=u'二次确认订单时需要用到')
    is_valid = IntField(verbose_name=u'是否确认过', default=0)
    is_tipped = BooleanField(verbose_name=u'是否点评过', default=False)
    is_active = BooleanField(verbose_name=u'是否删除', default=0)
    reason = StringField(verbose_name=u'取消理由',
                         help_text=u'财务页面查看订单取消的理由')  # old: reason
    cancel_date = DateTimeField(verbose_name=u'取消订单日期')
    source = StringField(verbose_name=u'订单来源', max_length=20,
                         choices=source_choices)
    bt_order_id = IntField(verbose_name=u'面包订单Id', help_text=u'面包购买自由行产品订单id')
    last_modified = DateTimeField(verbose_name=u'更新时间',
                                  default=datetime.datetime.utcnow)  # old: last_modified
    date_added = DateTimeField(verbose_name=u'创建时间',
                               default=datetime.datetime.utcnow)  # old: date_added
    is_updated = BooleanField(default=False)
    description = StringField(verbose_name=u'描述')

    custom_option = DictField(verbose_name=u'自定义信息')

    def get_total_price(self):
        total_price = 0
        for item in self.items:
            total_price += item.product_sku.get_price()
        return total_price

    def get_price(self):
        """
        :returns
            price 订单实际需要支付的总价格
            retail_price 所有单品的总市场零售价
            count 订单包含的item（）数量
        """
        price = 0
        retail_price = 0
        count = 0
        for item in self.items:
            count += 1
            price += item.product_sku.price
            retail_price += item.product_sku.get_retail_price

        return {
            'price': price,
            'retail_price': retail_price,
            'count': count,
            'total': price
        }

    def as_dict(self, exclude=set(), only=set()):
        dict_data = super(Order, self).as_dict(exclude=exclude, only=only)
        dict_data.update({'custom_option': self.custom_option})
        dict_data.update({'info': self.info})
        dict_data.update({'detail': self.detail})

        return dict_data

    def get_order_detail_dict(self):
        ret = self.__dict__.copy()
        if self.custom_option:
            for i in json.loads(self.custom_option):
                ret.update({
                    i['name_en']: i['value'],
                })
        # detail_info = get_object_or_None(OrderDetail, order=self)
        # if detail_info:
        #     ret.update(detail_info.__dict__.copy())
        return ret.update(self.detail)

    # TODO 迁移数据时，将以下信息保存在Order.info
    # 保险的info
    #   plan_type  保险订单的计划类型 int #TODO 销售属性(或许会影响到价格，如苹果白色一般会贵点),应该保存在ProductSKU里
    #   start_date 开始日期 date #TODO 保险订单中， 用户选择不同的日期，可以组成不同的SKU
    #   end_date 结束日期 date
    #   credentials_category 保险供应商交易号 str
    #   sex 性别 int 0 男 1 女
    #   credentials_no 证件号码 str , old : credentials_number
    #   birth_date 出生日期 date
    #   policy_no 保险单号 str , old: policyno
    #   proposal_no 保险供应商交易号 str,  old: proposalno

    # 酒店的info
    #   booked_date = DateTimeField(verbose_name=u'预定日期')
    #   hotel_name 酒店名称 str
    #   baby_count 婴儿数量 int
    #   waiting_date 候车日期 date
    #   waiting_time 候车时间 time
    #   hotel_address 酒店地址 str
    #
    # 机票info
    #   flight 航班号 str
    #   take_off_time 起飞时间 date
    #   arrive_time 到达时间 date
    #   admission_time 入场时间 date
    #   wechat 微信号 srt old: weixin
    #   get_on_address 上车地点 str
    #   delivery_location 送达地点 str
    #   shipping_address 收货地址/邮寄地址 str
    #   ticket_counter 自取网点 str
    #   hotel_address 酒店地址 str
    #   reservation_no 预约号(供应商确认页填写) str old:reservation_number
    #   waiting_address 侯车地点/上车地点(供应商确认页填写) str
    #   bar_code 条形码(供应商确认页) str
    #
    # 其他info
    #   use_date 使用日期， old：date
    #   supplier_voucher 供应商出游凭证 path

    # 索引配置
    # meta = {'indexes': [
    #     {'fields': ['$product', "$contact_name",'$product', '$contact_tel',
    #                 '$contact_email', '$status'],
    #      'default_language': 'english',
    #      'weights': {'title': 10, 'content': 2}
    #     }
    # ]}

    @classmethod
    def set_updated_time(cls, sender, document, **kwargs):
        document.updated_time = datetime.datetime.utcnow()

    meta = {
        'collection': 'vacation_order'
    }


signals.pre_save.connect(Order.set_updated_time, sender=Order)


class OrderTrack(Document):
    """
    订单跟踪
    """
    order = ReferenceField(Order)
    datetime = DateTimeField(verbose_name=u'处理时间')
    message = StringField(verbose_name=u'处理信息')
    operator = StringField(verbose_name=u'操作人')  # 比如 客户、系统、打包员、具体人名
